/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package edu.ccut.titan.component.impl;

import java.util.List;

import edu.ccut.titan.component.DefaultComponent;
import edu.ccut.titan.component.ILogicComponentInfo;
import edu.ccut.titan.component.ILogicContext;
import edu.ccut.titan.component.INodeInfo;
import edu.ccut.titan.component.IParameterContext;
import edu.ccut.titan.component.IParameterInfo;
import edu.ccut.titan.component.TitanComponentException;
import edu.ccut.titan.component.impl.node.EndNodeInfo;

/**
 * Logic component use to implement component Type with logic.
 * 
 * <p>
 * Logic component like a process for handle a business logic.
 * It can invoke base component for handle real technology or invoke a
 * sub-logic component. 
 * 
 * @author LeslieGu
 * 
 */
public class TitanLogicComponent extends DefaultComponent {

	private ILogicComponentInfo logicComponentInfo;

	public TitanLogicComponent(ILogicComponentInfo logicComponentInfo) {
		super(logicComponentInfo.getFullKey());
		this.logicComponentInfo = logicComponentInfo;
	}

	@Override
	public void execute(IParameterContext parameterContext) throws Exception {
		ILogicContext logicContext = this.createLogicContext(parameterContext);

		doExcute(logicContext);

		this.resetParameterContext(parameterContext, logicContext);
	}

	private ILogicContext createLogicContext(IParameterContext parameterContext) {
		INodeInfo startNodeInfo = this.logicComponentInfo.getStartNodeInfo();
		List<IParameterInfo> parameterInfos = startNodeInfo.getParameterInfos();

		TitanLogicContext logicContext = new TitanLogicContext(
				this.logicComponentInfo.getFullKey());

		for (int i = 0; i < parameterInfos.size(); ++i) {
			IParameterInfo parameterInfo = parameterInfos.get(i);
			logicContext.setValue(parameterInfo.getValueKey(), parameterContext
					.getParameter(i).getValue());
		}

		logger.debug("create logic context in process \"" + super.getComponentFullKey() + "\"");
		return logicContext;
	}

	//TODO:This is like the action component, can refact it?
	private void doExcute(ILogicContext logicContext) throws Exception {
		INodeInfo startNodeInfo = this.logicComponentInfo.getStartNodeInfo();

		String nextId = "start";
		INodeInfo nodeInfo = startNodeInfo;
		do {
			if (nodeInfo == null) {
				throw new TitanComponentException("can't find node id = "
						+ nextId + "in logic component "
						+ this.logicComponentInfo.getFullKey());
			}

			nodeInfo.execute(logicContext);
			
			if (nodeInfo instanceof EndNodeInfo) {
				break;
			}
			
			nextId = nodeInfo.getNextId();
			nodeInfo = this.logicComponentInfo.getNodeInfo(nextId);
		} while (nextId != null);
	}

	private void resetParameterContext(IParameterContext parameterContext,
			ILogicContext logicContext) {
		INodeInfo startNodeInfo = this.logicComponentInfo.getStartNodeInfo();

		List<IParameterInfo> parameterInfos = startNodeInfo.getParameterInfos();

		for (int i = 0; i < parameterInfos.size(); ++i) {
			IParameterInfo parameterInfo = parameterInfos.get(i);
			if (IParameterInfo.PASSING_OUT.equals(parameterInfo
					.getPassingType())
					|| IParameterInfo.PASSING_IN_OUT.equals(parameterInfo
							.getPassingType())) {
				
				parameterContext.setParameter(i, logicContext
						.getValue(parameterInfo.getValueKey()));
			}
		}
	}
}
